﻿using Polly;
using Polly.Timeout;
using System;
using System.Threading;

namespace MyLiPollyClient
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            Policy policy = Policy.Handle<ArgumentException>()  //故障1
                                  .Or<ArgumentOutOfRangeException>()  //故障2
                                  .Or<IndexOutOfRangeException>()     //故障3
                                  .Fallback(() =>
                                  {
                                      //降级执行的动作
                                      Console.WriteLine("我是降级后的执行的操作");                                      
                                  }, ex =>
                                  {
                                      //1.降级返回原业务报错信息
                                      Console.WriteLine($"业务报错信息为：{ex.Message}");
                                  });
            
            policy.Execute(() =>
            {
                //执行业务代码
                Console.WriteLine("开始任务");
                throw new ArgumentException("类型转换失败");
                Console.WriteLine("结束任务");
            });

            //降级业务中的返回值
            Policy<string> policy1 = Policy<string>.Handle<ArgumentException>()  //故障
                                  .Fallback(() =>
                                  {
                                      //降级执行的动作
                                      Console.WriteLine("我是降级后的执行的操作");
                                      return "我是降级业务中的返回值";
                                  });
            policy1.Execute(() =>
            {
                //执行业务代码
                Console.WriteLine("开始任务");
                throw new ArgumentException("类型转换失败");
                Console.WriteLine("结束任务");
            });

            //熔断
            //下面设置的是连续出错3次之后熔断10秒，意思是：连续出错3次后，熔断10s，在这10s内，再次访问，不再执行Execute中的代码，直接报错，
            //10s熔断时间过后，继续访问，如果还是出错(出一次即可)，直接熔断10s， 再次重复这个过程
            {
                Policy policyCircuitBreaker = Policy
                 .Handle<Exception>()
                 .CircuitBreaker(3, TimeSpan.FromSeconds(10));    //连续出错3次之后熔断10秒(不会再去尝试执行业务代码）。 

                while (true)
                {
                    Console.WriteLine("开始Execute");
                    try
                    {
                        policyCircuitBreaker.Execute(() =>
                        {
                            Console.WriteLine("-------------------------------------开始任务---------------------------------------");
                            throw new Exception();
                            Console.WriteLine("完成任务");
                        });
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("execute出错" + ex.Message);
                    }
                    Thread.Sleep(2000);
                }
            }            
        }

        static void PollRetry()
        {
            //重试
            {
                try
                {
                    Policy policyRetry = Policy
                    .Handle<Exception>()
                    .Retry(3); //出错后,连续执行3次
                    //.RetryForever();//出错后,连续执行,直到成功为止
                    //.WaitAndRetry(5, i => TimeSpan.FromSeconds(2));  //重试5次，每次间隔2s
                    int g = 0;
                    policyRetry.Execute(() =>
                    {
                        Console.WriteLine($"开始任务,g={g}");
                        if (g < 10)
                        {
                            g++;
                            throw new Exception("业务出错了");
                        }

                        Console.WriteLine("完成任务");
                    });
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"捕获异常:{ex.Message}");
                }
            }
        }

        static void TestTimeoutFallBack()
        {
            //1.1 超时3秒
            Policy policytimeout = Policy.Timeout(3, TimeoutStrategy.Pessimistic);
            //1.2 降级
            Policy policyFallBack = Policy.Handle<TimeoutRejectedException>()
               .Fallback(() =>
               {
                       //降级执行的动作
                       Console.WriteLine("我是降级后的执行的操作");
               }, ex =>
               {
                       //捕获业务中的出错信息
                       Console.WriteLine(ex.Message);
               });
            //1.3 将超时和降级操作进行组合
            Policy policy = policyFallBack.Wrap(policytimeout);
            //1.4 执行业务代码
            policy.Execute(() =>
            {
                Console.WriteLine("开始任务");
                Thread.Sleep(5000);
                Console.WriteLine("完成任务");
            });
        }

        static void TestRetryFallBack()
        {
            //2.1 遇到异常重试3次
            Policy policyRetry = Policy.Handle<Exception>().Retry(3);
            //2.2 降级操作
            Policy policyFallback = Policy.Handle<Exception>()
                                          .Fallback(() =>
                                          {
                                                  //降级执行的动作
                                                  Console.WriteLine("我是降级后的执行的操作");
                                          }, ex =>
                                          {
                                                  //捕获业务中的出错信息
                                                  Console.WriteLine(ex.Message);
                                          });
            //Wrap：包裹。policyRetry在里面，policyFallback在外面，如果里面出现了故障，则把故障抛出来给外面
            //2.3 进行包裹(出现错误,先重试3次,期间成功,则继续执行后面业务;期间失败,则走外层的降级操作)
            Policy policy = policyFallback.Wrap(policyRetry);
            int g = 0;
            //2.4 执行业务代码
            policy.Execute(() =>
            {
                Console.WriteLine($"开始任务,g={g}");
                if (g < 10)
                {
                    g++;
                    throw new Exception("业务出错了");
                }

                Console.WriteLine("完成任务");
            });
        }

        static void TestCircuitBreakerFallback()
        {
            //3.1 熔断
            Policy policyCBreaker = Policy.Handle<Exception>()
                                    .CircuitBreaker(3, TimeSpan.FromSeconds(10));    //连续出错3次之后熔断10秒(不会再去尝试执行业务代码）。 
                                                                                     //3.2 降级
            Policy policyFallback = Policy.Handle<Exception>()
                                   .Fallback(() =>
                                   {
                                           //降级执行的动作
                                           Console.WriteLine("我是降级后的执行的操作");
                                   }, ex =>
                                   {
                                           //这里是捕获业务代码中的错误，业务代码中就不要再写try-catch，否则不抛异常，则无法降级
                                           Console.WriteLine($"业务报错信息为：{ex.Message}");
                                   });
            //3.4 包裹
            Policy policy = policyFallback.Wrap(policyCBreaker);

            //3.4 执行业务
            while (true)
            {
                Console.WriteLine("开始Execute");
                //try
                //{
                policy.Execute(() =>
                {
                    Console.WriteLine("-------------------------------------开始任务---------------------------------------");
                    throw new Exception();
                    Console.WriteLine("完成任务");
                });
                //}
                // 不要再写try-catch，否则不抛异常，则无法降级
                //catch (Exception ex)
                //{
                //    Console.WriteLine("execute出错" + ex.Message);
                //}
                Thread.Sleep(2000);
            }
        }

    }
}
